<?php

namespace Trawind\Basics\Exceptions;

use Exception;
use Illuminate\Http\Response;
use Illuminate\Support\Str;
use Trawind\Helpers\SystemHelper;
use Trawind\Helpers\WechatHelper;
use Trawind\Helpers\ResponseHelper;
use Trawind\Basics\Enums\BusinessCode;
use GuzzleHttp\Exception\TransferException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{

    protected static $jsonMap = [];

    protected $dontReport = [
        ForbiddenException::class,
        LogicException::class,
    ];


    public function report(Exception $e)
    {
        parent::report($e);
        if ($this->shouldReport($e)) {
            $data = self::getExceptionJsonMap($e);
            if (
                $data['code'] === BusinessCode::SYSTEM_ERROR
                && !SystemHelper::runningInConsole()
                && !Str::contains(request()->header('User-Agent'),'Postman')
            ) {
                WechatHelper::factory()
                    ->systemExceptionNotice($data['message'], $data['code'], $data['trace'], get_class($e))
                    ->withDeveloper();
            }
        }
    }


    protected function prepareJsonResponse($request, Exception $e)
    {
        return ResponseHelper::cors(parent::prepareJsonResponse($request, $e));
    }


    protected static function getExceptionJsonMap(Exception $e): array
    {
        if (empty(self::$jsonMap)) {
            self::$jsonMap = [
                'code' => BusinessCode::SYSTEM_ERROR,
                'message' => $e->getMessage(),
                'trace' => $e->getTrace(),
            ];

            switch ($e) {
                case $e instanceof NotFoundHttpException:
                    self::$jsonMap['code'] = BusinessCode::NOT_FOUND_ERROR;
                    self::$jsonMap['message'] = $e->getMessage() ?: 'Not Found('.urldecode(url()->full()).')';
                    break;

                case $e instanceof ModelNotFoundException:
                    self::$jsonMap['code'] = BusinessCode::NOT_FOUND_ERROR;
                    self::$jsonMap['message'] = ___('resource_id_not_found').join(',', $e->getIds());
                    break;

                case $e instanceof \League\OAuth2\Server\Exception\OAuthServerException:
                case $e instanceof \Laravel\Passport\Exceptions\OAuthServerException:
                self::$jsonMap['code'] = BusinessCode::TOKEN_EXPIRED;
                    break;

                case $e instanceof TransferException:
                    $content = self::$jsonMap;
                    if (method_exists($e, 'getResponse') && !is_null($e->getResponse())) {
                        $content = $e->getResponse()->getBody()->getContents();
                        $content = json_decode($content, true);
                        //                    $content['status'] = $e->getResponse()->getStatusCode();
                    }
                    self::$jsonMap = $content;
            }
        }
        return self::$jsonMap;
    }


    protected function convertExceptionToArray(Exception $e)
    {
        $data = self::getExceptionJsonMap($e);
        return config('app.debug') ? [
            'code' => $data['code'],
            'message' => $data['message'],
            'exception' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $data['trace'],
            //            'trace' => collect($e->getTrace())->map(function ($trace) {
            //                return Arr::except($trace, ['args']);
            //            })->all(),
        ] : [
            'code' => $data['code'],
            'message' => $this->isHttpException($e) ? $data['message'] : 'Server Error',
        ];
    }


    protected function invalidJson($request, ValidationException $exception)
    {
        $keys = $exception->validator->errors()->keys();
        $message = $exception->validator->errors()->first();
        if (($line = preg_match('/^.[1-9]\d*.$/', $keys[0])) !== false) {
            $message = str_replace(':line', ++$line, $message);
        };

        return ResponseHelper::exception(
            BusinessCode::PARAMETER_ERROR,
            $message,
            $exception->status,
            config('app.debug') ? ['errors' => $exception->errors()] : []
        );
    }


    protected function unauthenticated($request, AuthenticationException $exception)
    {
        return ResponseHelper::exception(
            BusinessCode::TOKEN_EXPIRED,
            $exception->getMessage(),
            Response::HTTP_UNAUTHORIZED
        );
    }

}
